/* Hello World Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "driver/uart.h"
#include "driver/gpio.h"

#include "esp_http_server.h"

#include "esp_event.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "esp_log.h"

#include "dirent.h"
#include "esp_vfs.h"
#include "esp_spiffs.h"
#include "ff.h"
#include "nvs.h"
#include "nvs_flash.h"


#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"




static TaskHandle_t lua_handler;
static lua_State *L;
static void lua_task(void *pvParameter);



static int lua_testf(lua_State* L){
  lua_pushinteger(L,123);
  lua_pushstring(L,"hello this is lua");
  return 2;
} 

static int lua_delay(lua_State* L){
  int op1 = luaL_checkinteger(L,1);
  vTaskDelay(op1/portTICK_RATE_MS);
  return 0;
}

static int lua_digitalWrite(lua_State* L){
  int op1 = luaL_checkinteger(L,1);
  int op2 = luaL_checkinteger(L,2);
  gpio_set_level(op1, op2);
  return 0;
}

static int lua_pinMode(lua_State* L){
  int op1 = luaL_checkinteger(L,1);
  int op2 = luaL_checkinteger(L,2);
  if(op2==0){
    gpio_set_direction(op1, GPIO_MODE_DISABLE);
  }else if(op2==1){
    gpio_set_direction(op1, GPIO_MODE_INPUT);
  }else if(op2==2){
    gpio_set_direction(op1, GPIO_MODE_OUTPUT);
  }else if(op2==3){
    gpio_set_direction(op1, GPIO_MODE_OUTPUT_OD);
  }
  
  return 0;
}

static const struct luaL_Reg mylib[]=
{
  {"test",lua_testf},
  {"delay",lua_delay},
  {"pinMode",lua_pinMode},
  {"digitalWrite",lua_digitalWrite},
  {NULL,NULL}
};

 char*lua_test="print('---lua------')\r\n"
                "pinMode(2,2)\r\n"
                "while(true)\r\n"
                "do\r\n"
                "    digitalWrite(2,0)\r\n"
                "    delay(1000)\r\n"
                "    digitalWrite(2,1)\r\n"
                "    delay(1000)\r\n"
                "end\r\n"
                "";
  //              "    print(name,'hello')\r\n"
static const char *TAG = "example";

void spiffsInit(){

   ESP_LOGI(TAG, "Initializing SPIFFS");
    
    esp_vfs_spiffs_conf_t conf = {
      .base_path = "",
      .partition_label = NULL,
      .max_files = 5,
      .format_if_mount_failed = true
    };
    
    // Use settings defined above to initialize and mount SPIFFS filesystem.
    // Note: esp_vfs_spiffs_register is an all-in-one convenience function.
    esp_err_t ret = esp_vfs_spiffs_register(&conf);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount or format filesystem");
        } else if (ret == ESP_ERR_NOT_FOUND) {
            ESP_LOGE(TAG, "Failed to find SPIFFS partition");
        } else {
            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
        }
        return;
    }
    
    size_t total = 0, used = 0;
    ret = esp_spiffs_info(NULL, &total, &used);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
    } else {
        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
    }
    // 列出目录信息
    DIR* dir = opendir("");
    while (true) {
        struct dirent* de = readdir(dir);
        if (!de) {
            break;
        }
        printf("file:%s\r\n",de->d_name);
        struct stat st;
        if (stat(de->d_name, &st) == 0) {
            printf("file size:%ld\r\n",st.st_size);
            // Delete it if it exists
            //unlink("/spiffs/foo.txt");
        }

    }


    // ESP_LOGI(TAG, "Opening file");
    // FILE* f = fopen("/hello.txt", "w");
    // if (f == NULL) {
    //     ESP_LOGE(TAG, "Failed to open file for writing");
    //     return;
    // }
    // fprintf(f, "Hello World!\n");
    // fclose(f);
    // ESP_LOGI(TAG, "File written");
}
void printInfo(){

   /* Print chip information */
    esp_chip_info_t chip_info;
    esp_chip_info(&chip_info);
    printf("This is ESP8266 chip with %d CPU cores, WiFi, ",
            chip_info.cores);

    printf("silicon revision %d, ", chip_info.revision);

    printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024),
            (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");

    // for (int i = 10; i >= 0; i--) {
    //     printf("Restarting in %d seconds...\n", i);
    //     vTaskDelay(1000 / portTICK_PERIOD_MS);
    // }
    printf("Restarting now.\n");
    //fflush(stdout);
    //esp_restart();
    //dup2(1,1);

}

static void mem_task(void *pvParameter){


  //gpio_set_direction(GPIO_NUM_2, GPIO_MODE_OUTPUT);//把这个GPIO作为输出 
  while(1){
    uint32_t  mem= esp_get_free_heap_size();
    //printf("mem:\t%d \r\n",mem);
    //gpio_set_level(GPIO_NUM_2, 0);//把这个GPIO输出低电平  
    //vTaskDelay(1000 / portTICK_PERIOD_MS);
   // gpio_set_level(GPIO_NUM_2, 1);//把这个GPIO输出低电平  
    vTaskDelay(1000 / portTICK_PERIOD_MS);
  }
  vTaskDelete(NULL);
}



/* An HTTP GET handler */
esp_err_t hello_get_handler(httpd_req_t *req)
{
    char*  buf;
    size_t buf_len;
    buf_len = httpd_req_get_url_query_len(req) + 1;
    if (buf_len > 1) {
        buf = malloc(buf_len);
        if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
            char param[32];
            if (httpd_query_key_value(buf, "opt", param, sizeof(param)) == ESP_OK) {
                if(strcmp("restart",param)==0){

                }else if(strcmp("start",param)==0){
                    char*fileName=(char*)malloc(20);
                    char*temp=fileName;
                    for(char i=0;i<20;i++){
                        *temp=0;
                        temp++;
                    }
                    if (httpd_query_key_value(buf, "name", fileName, 19) != ESP_OK) {
                        free(fileName);
                    }
                    vTaskSuspend(lua_handler);
                    vTaskDelete(lua_handler);
                    lua_close(L);
                    xTaskCreate(lua_task, "lua_task", 2048*2, fileName, 1, &lua_handler);
                }else if(strcmp("stop",param)==0){
                    printf("=====stop\r\n");
                    
                    //暂停任务，然后停止任务
                    vTaskSuspend(lua_handler);
                    vTaskDelete(lua_handler);
                    lua_close(L);
                    uint32_t  mem= esp_get_free_heap_size();
                    printf("mem:\t%d \r\n",mem);
                }
            }
        }
        if(buf!=NULL){
            free(buf);
        }
        
    }
    const char* resp_str = (const char*) req->user_ctx;
    httpd_resp_send(req, resp_str, strlen(resp_str));
    if (httpd_req_get_hdr_value_len(req, "Host") == 0) {
        ESP_LOGI(TAG, "Request headers lost");
    }
    return ESP_OK;
}


httpd_uri_t hello = {
    .uri       = "/hello",
    .method    = HTTP_GET,
    .handler   = hello_get_handler,
    .user_ctx  = "Hello World!"
};

//#define MIN(X,Y) (X<Y?X:Y)


//文件上传
esp_err_t file_upload_post_handler(httpd_req_t *req)
{
    char buf[100];
    int ret, remaining = req->content_len;

    httpd_req_get_hdr_value_str(req,"fileName",buf,sizeof(buf));
    printf("file name:%s\r\n",buf);
    FILE* f = fopen(buf, "w");
    
    
    while (remaining > 0) {
        /* Read the data for the request */
        if ((ret = httpd_req_recv(req, buf,
                        MIN(remaining, sizeof(buf)))) <= 0) {
            if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
                /* Retry receiving if timeout occurred */
                continue;
            }
            return ESP_FAIL;
        }

        fwrite(buf,1,ret,f);
        remaining -= ret;
    }
    fclose(f);
    sprintf(buf,"file upload %s","ok");
    ret=strlen(buf);
    httpd_resp_send_chunk(req, buf, ret);
    httpd_resp_send_chunk(req, NULL, 0);
    return ESP_OK;
}

httpd_uri_t fileUpload = {
    .uri       = "/fileUpload",
    .method    = HTTP_POST,
    .handler   = file_upload_post_handler,
    .user_ctx  = NULL
};


//注册http服务器
static httpd_handle_t server = NULL;
static void httpServer(){
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();

    // Start the httpd server
    ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
    if (httpd_start(&server, &config) == ESP_OK) {
        // Set URI handlers
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &hello);
        httpd_register_uri_handler(server, &fileUpload);
    }

}


static ip4_addr_t s_ip_addr;

static size_t  lua_print_str_temp(const char *s,size_t l){
  printf("%s",s);
  return 0;
}

static void wifi_state_handler(void *arg, esp_event_base_t event_base,
                               int32_t event_id, void *event_data)
{
  char*p=(char*)arg;
  printf("########wifi_state_handler %d\r\n",*p);
  if(*p==1){
      //连接失败
      printf("########on_wifi_disconnect\r\n");
      system_event_sta_disconnected_t *event = (system_event_sta_disconnected_t *)event_data;

      ESP_LOGI(TAG, "Wi-Fi disconnected, trying to reconnect...");
      if (event->reason == WIFI_REASON_BASIC_RATE_NOT_SUPPORT) {
          /*Switch to 802.11 bgn mode */
          esp_wifi_set_protocol(ESP_IF_WIFI_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N);
      }
      esp_wifi_connect();
  }else if(*p==2){
    //连接上wifi
    printf("########on_wifi_connect %d\r\n",*p);
    ESP_LOGI(TAG, "Wi-Fi connected ");
  }else if(*p==3){
    //获取到ip地址
    ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
    memcpy(&s_ip_addr, &event->ip_info.ip, sizeof(s_ip_addr));

    u32_t  ip=s_ip_addr.addr;
    //printf("########on_got_ip\r\n");
    //printf("ip:%d.%d.%d.%d\r\n",(ip)&0x0FF,(ip>>8)&0x0FF,(ip>>16)&0x0FF,(ip>>24)&0x0FF);


    tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_AP;
    tcpip_adapter_ip_info_t ip_info;
    tcpip_adapter_get_ip_info(tcpip_if,&ip_info);
    ip=ip_info.ip.addr;
    printf("AP-ip:%d.%d.%d.%d\r\n",(ip)&0x0FF,(ip>>8)&0x0FF,(ip>>16)&0x0FF,(ip>>24)&0x0FF);


    tcpip_if = TCPIP_ADAPTER_IF_STA;
    tcpip_adapter_get_ip_info(tcpip_if,&ip_info);
    ip=ip_info.ip.addr;
    printf("ip:%d.%d.%d.%d\r\n",(ip)&0x0FF,(ip>>8)&0x0FF,(ip>>16)&0x0FF,(ip>>24)&0x0FF);
    httpServer();
    printf("#### http server start...\r\n");

    //重定向lua的输入输出函数数据
    lua_print_str=lua_print_str_temp;
  }
   
}




static char ssid[32] ="biaodiankeji";
static char password[32] ="bdkj6666";
static char p[]={1,2,3};
void connectWifi(){


    nvs_flash_init();
    esp_netif_init();
    esp_event_loop_create_default();
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);

    esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &wifi_state_handler, &p[0]);
    esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &wifi_state_handler, &p[1]);
    esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_state_handler, &p[2]);

    
    esp_wifi_set_storage(WIFI_STORAGE_RAM);
    wifi_config_t wifi_config = { 0 };

    strncpy((char *)&wifi_config.sta.ssid, ssid, 32);
    strncpy((char *)&wifi_config.sta.password, password, 32);

    esp_wifi_set_mode(WIFI_MODE_STA);
    esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);
    esp_wifi_start();
    esp_wifi_connect();
}

static void lua_task(void *pvParameter){
    printf("-----task1 exec------\r\n");
    char*fileName=(char*)pvParameter;
    if(fileName!=NULL){
        printf("fileName:%s\r\n",fileName);
         struct stat st;
        if (stat(fileName, &st) == 0) {
            printf("file size:%ld\r\n",st.st_size);
            lua_test=(char*)malloc(st.st_size+1);
            for(int i=0;i<st.st_size+1;i++){
                lua_test[i]=0;
            }
             FILE* f = fopen(fileName, "r");
             fread(lua_test,1,st.st_size,f);
             fclose(f);
             free(fileName);
        }
    }
    
    L=luaL_newstate();
    //luaopen_io(L);
  	luaopen_base(L);
    //luaL_openlibs(L);
    
    luaL_setfuncs(L,mylib,0);
    lua_pushstring(L,"task1");
    lua_setglobal(L,"name");

    if(luaL_dostring(L, lua_test)!=LUA_OK){
        const char* err = lua_tostring(L, -1);
        printf(err);
        printf("\r\n\r\n");
    }
    printf("0000000000\r\n");
    lua_close(L);
    printf("#### close task\r\n");
    vTaskDelete(NULL);
}
char* luaFileName="hello.lua";
void app_main()
{
    uart_set_baudrate(UART_NUM_0,74880);
    
    printf("Hello world!\n");
    xTaskCreate(mem_task, "mem_task", 2048, NULL, 1, NULL);
    connectWifi();
    spiffsInit();
    printInfo();

    xTaskCreate(lua_task, "lua_task", 2048*8, luaFileName, 1, &lua_handler);
    UBaseType_t num=uxTaskGetNumberOfTasks();
    printf("task num %d, size:%d\r\n",num,sizeof(TaskStatus_t));
    TaskStatus_t *task=(TaskStatus_t*)malloc(sizeof(TaskStatus_t)*num);
    UBaseType_t temp=uxTaskGetSystemState(task,num,NULL);
    printf("state:%d\r\n",temp);
    if(temp>0){
        TaskStatus_t*p=NULL;
        for(char i=0;i<temp;i++){
            p=task+i;
            printf("task name:%s\r\n",p->pcTaskName);
            
        }
    }
    free(task);
    // char InfoBuffer[200];
    // vTaskList(InfoBuffer);
    // printf("taskName\ttaskState\ttaskPrio\ttaskStack\ttaskNum\r\n");
    // printf("%s",InfoBuffer);
}
